package WekaRelated;

import java.util.ArrayList;

import weka.classifiers.Evaluation;
import weka.classifiers.evaluation.EvaluationUtils;
import weka.core.Attribute;
import weka.core.DenseInstance;
import weka.core.Instances;
import Definitions.GraphClass;
import Definitions.NodeClass;
import Global.ConstantVariable;
import Global.GlobalClass;
import InputPreparer.InputPreparationMethodInterface;
import LocalClassifier.MachineLearningClassifiersClass;
import Result.ResultClass;
import Sampling.SamplingAbstractClass;

public class GenericWekaClassifierClass extends MachineLearningClassifiersClass{
	String name;
	ArrayList <InputPreparationMethodInterface> inputPreparer;
	
	weka.classifiers.Classifier localClassifier;
	SamplingAbstractClass currentSampling;
	int dimensionOfXVektor;
	
	Evaluation eval;
	EvaluationUtils evalUtilities;
	
	ResultClass result;
	String useThisClassifierInsteadOfStaticOne;
	
	GlobalClass global;
	
	public GenericWekaClassifierClass(String name, ArrayList<InputPreparationMethodInterface> inputPrep, SamplingAbstractClass currentSampling, GlobalClass global) 
	{
		super(currentSampling, global);
		// TODO Auto-generated constructor stub
		this.inputPreparer=inputPrep;
		this.name=name;
		this.currentSampling = currentSampling;
		this.global = global;	
		dimensionOfXVektor = 0;
		localClassifier = null;
		useThisClassifierInsteadOfStaticOne = ConstantVariable.WekaClassifiers.SELECTED_WEKA_CLASSIFIER;
	}
	
	public void changeSecondLevelClassifierToBeUsed(String newClassifierName)
	{
		useThisClassifierInsteadOfStaticOne = newClassifierName; 
	}
	
	public String getWekaClassifierToBeUsed()
	{
		return useThisClassifierInsteadOfStaticOne; 
	}
	
	public void initialize(GraphClass g, ResultClass result)
	{
		NodeClass u = g.getDataSet().get(0);
		this.dimensionOfXVektor= getInformationVector( u, ConstantVariable.Sampling_Constants.NODE_IS_IN_TRAINING_SET_FOR_THE_FOLD).size();
		System.out.println("Size of the input feature vector:" + this.dimensionOfXVektor);
		this.result = result;				
	}	

	public void train(GraphClass graph, SamplingAbstractClass sampling)  
	{
		try {
			Instances trainingInstances = generateInstanceOnTheFlyWithRequiredVector(currentSampling.getTrainAndValNodes(), ConstantVariable.Sampling_Constants.TRAINING_STAGE);
			Instances testInstances = generateInstanceOnTheFlyWithRequiredVector(currentSampling.getTestNodes(), ConstantVariable.Sampling_Constants.TEST_STAGE);
	
			trainingInstances.setClassIndex(trainingInstances.numAttributes() - 1);
			testInstances.setClassIndex(trainingInstances.numAttributes() - 1);
			localClassifier = WekaAdaptorClass.getNewLocalClassifier(useThisClassifierInsteadOfStaticOne, null);
	
			if(localClassifier == null)
			{
				System.out.println("localClassifier=null:There is a problem with the selected Weka Classifier.");
				System.exit(0);
			}

		
			localClassifier.buildClassifier(trainingInstances);
		}
		catch (Exception e) 
		{
			e.printStackTrace();
			System.exit(-1);
		}

		
	}
	
	public void train(GraphClass graph)  
	{
		try {
		
			//Instances trainingInstances = generateInstanceOnTheFlyWithRequiredVector(currentSampling.getTrainNodes(), ConstantVariable.Sampling_Constants.TRAINING_STAGE);			
			Instances trainingInstances = generateInstanceOnTheFlyWithRequiredVector(currentSampling.getTrainAndValNodes(), ConstantVariable.Sampling_Constants.TRAINING_STAGE);
			Instances testInstances = generateInstanceOnTheFlyWithRequiredVector(currentSampling.getTestNodes(), ConstantVariable.Sampling_Constants.TEST_STAGE);
			
			
			
			//System.out.println("Num of trainingInstances:"+trainingInstances.numInstances());
			//System.out.println("Num of testInstances:"+testInstances.numInstances());
			// setting class attribute
			trainingInstances.setClassIndex(trainingInstances.numAttributes() - 1);
			testInstances.setClassIndex(trainingInstances.numAttributes() - 1);
			
			//Instances filteredTrainingInstances = WekaAdaptorClass.useWekaFilter(trainingInstances);
			
			//System.out.println("# of Attr filteredTrainingInstances:"+filteredTrainingInstances.numAttributes());

			localClassifier = WekaAdaptorClass.getNewLocalClassifier(useThisClassifierInsteadOfStaticOne, null);

			if(localClassifier == null)
			{
				System.out.println("localClassifier=null:There is a problem with the selected Weka Classifier.");
				System.exit(0);
			}

			localClassifier.buildClassifier(trainingInstances);
			//double classLabelEstimated = localClassifier.classifyInstance(testInstances.get(0));
			//System.out.println("classLabelEstimated:"+classLabelEstimated+ "act:"+currentSampling.getTestNodes().get(0).getClassOrder());
			//FastVector<Double> ResultsForTest = evalUtilities.getTestPredictions(localClassifier, testInstances);
			
			
			
			//eval = new Evaluation(trainingInstances);
			//eval.evaluateModel(localClassifier, testInstances);
			//result.setEvaluationResultOfWeka(eval);
			//testInstances.get(0).
			/*
			 System.out.print("Acc:"+eval.pctCorrect());
			 System.out.print(" Inc.:"+eval.pctIncorrect());
			 System.out.println(" Err Rate:"+eval.errorRate());
			 System.out.println(" FMeasure(0):"+eval.fMeasure(0));
			 System.out.println(" FMeasure(1):"+eval.fMeasure(1));
			 System.out.println(" Precision(0):"+eval.precision(0));
			 System.out.println(" Precision(1):"+eval.precision(1));			 
			 System.out.println(" Recall(0):"+eval.recall(0));
			 System.out.println(" Recall(1):"+eval.recall(1));*/			 

			 //System.out.println(eval.toSummaryString("\nResults\n======\n", false));
		
		} catch (Exception e) 
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}   // build classifier
	}
	
	public void trainAndTestWithTheGivenNodeLists(ArrayList<NodeClass> nodeListForTrain, ArrayList<NodeClass> nodeListForTest)  
	{
		try {
		
			//Instances trainingInstances = generateInstanceOnTheFlyWithRequiredVector(currentSampling.getTrainNodes(), ConstantVariable.Sampling_Constants.TRAINING_STAGE);
			Instances trainingInstances = generateInstanceOnTheFlyWithRequiredVector(nodeListForTrain, ConstantVariable.Sampling_Constants.TRAINING_STAGE);
			Instances testInstances = generateInstanceOnTheFlyWithRequiredVector(nodeListForTest, ConstantVariable.Sampling_Constants.TEST_STAGE);
			
			System.out.println("Num of trainingInstances:"+trainingInstances.numInstances());
			System.out.println("Num of testInstances:"+testInstances.numInstances());
			// setting class attribute
			trainingInstances.setClassIndex(trainingInstances.numAttributes() - 1);
			testInstances.setClassIndex(trainingInstances.numAttributes() - 1);
			
			//Instances filteredTrainingInstances = WekaAdaptorClass.useWekaFilter(trainingInstances);
			
			//System.out.println("# of Attr filteredTrainingInstances:"+filteredTrainingInstances.numAttributes());

			localClassifier = WekaAdaptorClass.getNewLocalClassifier(useThisClassifierInsteadOfStaticOne, null);

			if(localClassifier == null)
			{
				System.out.println("There is a problem with the selected Weka Classifier.");
				System.exit(0);
			}

			localClassifier.buildClassifier(trainingInstances);
			//double classLabelEstimated = localClassifier.classifyInstance(testInstances.get(0));
			//System.out.println("classLabelEstimated:"+classLabelEstimated+ "act:"+currentSampling.getTestNodes().get(0).getClassOrder());
			//FastVector<Double> ResultsForTest = evalUtilities.getTestPredictions(localClassifier, testInstances);
			
			
			
			eval = new Evaluation(trainingInstances);
			eval.evaluateModel(localClassifier, testInstances);
			result.setEvaluationResultOfWeka(eval);
			//testInstances.get(0).
			/*
			 System.out.print("Acc:"+eval.pctCorrect());
			 System.out.print(" Inc.:"+eval.pctIncorrect());
			 System.out.println(" Err Rate:"+eval.errorRate());
			 System.out.println(" FMeasure(0):"+eval.fMeasure(0));
			 System.out.println(" FMeasure(1):"+eval.fMeasure(1));
			 System.out.println(" Precision(0):"+eval.precision(0));
			 System.out.println(" Precision(1):"+eval.precision(1));			 
			 System.out.println(" Recall(0):"+eval.recall(0));
			 System.out.println(" Recall(1):"+eval.recall(1));*/			 

			 //System.out.println(eval.toSummaryString("\nResults\n======\n", false));
		
		} catch (Exception e) 
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}   // build classifier
	}
	
	public void trainWithTheGivenNodeList(ArrayList<NodeClass> nodeListForTrain)  
	{
		try {
		
			//Instances trainingInstances = generateInstanceOnTheFlyWithRequiredVector(currentSampling.getTrainNodes(), ConstantVariable.Sampling_Constants.TRAINING_STAGE);
			Instances trainingInstances = generateInstanceOnTheFlyWithRequiredVector(nodeListForTrain, ConstantVariable.Sampling_Constants.TRAINING_STAGE);
						
			System.out.println("Num of trainingInstances:"+trainingInstances.numInstances());
			// setting class attribute
			trainingInstances.setClassIndex(trainingInstances.numAttributes() - 1);
			localClassifier = WekaAdaptorClass.getNewLocalClassifier(useThisClassifierInsteadOfStaticOne, null);

			if(localClassifier == null)
			{
				System.out.println("There is a problem with the selected Weka Classifier.");
				System.exit(0);
			}

			localClassifier.buildClassifier(trainingInstances);
			eval = new Evaluation(trainingInstances);

		
		} catch (Exception e) 
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}   // build classifier
	}

	
	public double[] evaluate(NodeClass u, int stage, String s)
	{
		try {			
			
			ArrayList<NodeClass> oneNodeList= new ArrayList<NodeClass>();
			oneNodeList.add(u);

			Instances testInstances = generateInstanceOnTheFlyWithRequiredVector(oneNodeList, stage);
			testInstances.setClassIndex(testInstances.numAttributes() - 1);
			//double classLabelEstimated = localClassifier.classifyInstance(testInstances.get(0));
			//currentSampling.setClassLabelEstimated(u, (int)classLabelEstimated);
			
			double[] classLabelProbabilityDistribution = localClassifier.distributionForInstance(testInstances.get(0));
			
			double classLabelEstimated = Utility.WorkerUtilityClass.findMaxIndexForTheGivenVector(classLabelProbabilityDistribution);
			currentSampling.setClassLabelEstimated(u, (int)classLabelEstimated);
			/*System.out.println("classLabelProbabilityDistributionSize:"+classLabelProbabilityDistribution.length);
			
			for(int i=0; i<classLabelProbabilityDistribution.length; i++)
				System.out.print(classLabelProbabilityDistribution[i]+"  ");
			
			System.out.println("classLabelE:"+classLabelEstimated);*/
			return classLabelProbabilityDistribution;
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		/*
		Instances testInstances = generateInstanceOnTheFlyWithRequiredVector(currentSampling.getTrainNodes(), ConstantVariable.Sampling_Constants.TEST_STAGE);
		
		try {
			
			
			eval.crossValidateModel(localClassifier, testInstances, ConstantVariable.WekaEvaluationParameters.NUMBER_OF_FOLDS_FOR_CROSS_VALIDATION, new Random(1));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
		return null;
	}
	

	
	//generateInstanceOnTheFlyWithRequiredVector(ArrayList<NodeClass> nodeList, ConstantVariable.Sampling_Constants.TRAINING_STAGE);
	
	public Instances generateInstanceOnTheFlyWithRequiredVector(ArrayList<NodeClass> nodeList, int stage)
	{
		int numberOfFeatures;
		
		NodeClass u = nodeList.get(0);
		
		numberOfFeatures = getInformationVector(u, stage).size();
		
		try {
			
			ArrayList<Attribute> attributesList = new ArrayList<Attribute>();

			for(int i=0; i<numberOfFeatures; i++)
			{
				attributesList.add(new Attribute("Attribute "+i));			
			}
			
			 // -- nominal
			
			ArrayList<String> classValues = new ArrayList<String>();
			
			for(int i=0; i<global.classList.size(); i++)
			{
				classValues.add(global.classList.get(i).getName());			
			}			
			
			attributesList.add(new Attribute("Class", classValues));			
			
			Instances data = new Instances("AS_ON_THE_FLY_DATASET", attributesList, 0);

			int sizeOfTheDatasetOfTheGraph = nodeList.size();
			
			ArrayList<Double> currentAttributeList;

			int[] actualLabels = currentSampling.getActualClassLabelsForThisFold();

			for(int i=0; i<sizeOfTheDatasetOfTheGraph; i++)
			{
				double[] values = new double[numberOfFeatures+1];
				currentAttributeList = getInformationVector(nodeList.get(i), stage);
				
				for(int j=0; j<numberOfFeatures; j++)
				{					
					values[j] = currentAttributeList.get(j);					
				}
				
				values[numberOfFeatures] = actualLabels[nodeList.get(i).getOrder()];
//				values[numberOfFeatures] = nodeList.get(i).getClassOrder();
				// add
			    data.add(new DenseInstance(1.0, values));
			}

			return data;
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("Null donuyor");
		return null;
	}
	
	public Instances generateDatasetOnTheFlyWithGetInformationVector(GraphClass graph, int stage)
	{					
		currentSampling.setAllTheGraphAsTrainingSetForDebuggingPurposes();
		return generateInstanceOnTheFlyWithRequiredVector(graph.getDataSet(), stage);	
	}

	
	public  ArrayList<Double> getInformationVector(NodeClass u, int whichNodesToUseForAggregation)
	{
		
		ArrayList<Double> input = new ArrayList<Double>(); 

		for(int i=0;  i<inputPreparer.size() ; i++)
		{
			
			ArrayList <Double> tempInput = inputPreparer.get(i).getInput(u, whichNodesToUseForAggregation);
			
			for(int j=0 ; j< tempInput.size() ; j++)
			{
				input.add(tempInput.get(j));
			}

			tempInput.clear();
		}
		return input;
	}
}
